/*
//
//                  INTEL CORPORATION PROPRIETARY INFORMATION
//     This software is supplied under the terms of a license agreement or
//     nondisclosure agreement with Intel Corporation and may not be copied
//     or disclosed except in accordance with the terms of that agreement.
//          Copyright(c) 2004-2007 Intel Corporation. All Rights Reserved.
//
//     Intel Integrated Performance Primitives AAC Encode Sample for Windows*
//
//  By downloading and installing this sample, you hereby agree that the
//  accompanying Materials are being provided to you under the terms and
//  conditions of the End User License Agreement for the Intel Integrated
//  Performance Primitives product previously accepted by you. Please refer
//  to the file ippEULA.rtf or ippEULA.txt located in the root directory of your Intel IPP
//  product installation for more information.
//
//  MPEG-4 and AAC are international standards promoted by ISO, IEC, ITU, ETSI
//  and other organizations. Implementations of these standards, or the standard
//  enabled platforms may require licenses from various entities, including
//  Intel Corporation.
//
*/

#include "umc_defs.h"

#if defined (UMC_ENABLE_AAC_INT_AUDIO_ENCODER)

#include <math.h>
#include <stdio.h>
#include "ippac.h"
#include "ipps.h"
#include "aac_enc_quantization_int.h"
#include "aac_enc_search.h"

/****************************************************************************/

typedef struct
{
  Ipp32s x_min[N_LONG/2];
  Ipp32s x_min_pred[N_LONG/2];
  Ipp32s distortion[MAX_SECTION_NUMBER];
  Ipp16s saved_scale_factors[MAX_SECTION_NUMBER];
  Ipp32s saved_pred_used[MAX_SECTION_NUMBER];
  Ipp32s amplified[MAX_SECTION_NUMBER];
  Ipp32s max_dist;
  Ipp32s sum_dist;
  Ipp32s above_dist;
  Ipp32s above;
  Ipp32s max_win;
  Ipp32s max_sfb;
} sDistortionBlock;


/****************************************************************************/

static void aaciencMain_loop(sQuantizationBlock* pBlock,
                             sEnc_individual_channel_stream* pStream,
                             Ipp16s* mdct_line,
                             Ipp16s* mdct_scaled,
                             Ipp16s* mdct_line_pred,
                             Ipp16s* mdct_scaled_pred,
                             Ipp32s pow34_scaleFact);

static void aaciencCalcDist(sQuantizationBlock* pBlock,
                            sEnc_individual_channel_stream* pStream,
                            sDistortionBlock* pDist,
                            Ipp32s* mdct_line_int,
                            Ipp32s* mdct_line_pred_int,
                            Ipp32s  mdct_scaleFactor);

static Ipp32s aaciencAdjustSF(sQuantizationBlock* pBlock,
                           sEnc_individual_channel_stream* pStream,
                           sDistortionBlock* pDist,
                           Ipp16s* mdct_scaled,
                           Ipp32s* mdct_line_pred_int,
                           Ipp16s* mdct_scaled_pred,
                           Ipp32s*    pow34_scaleFact);

#define QUANT_ITER_NUM 8

/****************************************************************************/

Ipp16s scalefac_pow[] = {
  32767, 18658, 21247, 24196, 27554, 31379, 17867, 20347,
  23170, 26386, 30048, 17109, 19484, 22188, 25268, 28774
};

Ipp16s scalefac_pow_shift[] = {
  0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3
};

/****************************************************************************/

void aaciencQuantization(sQuantizationBlock* pBlock,
                         sEnc_individual_channel_stream* pStream,
                         Ipp16s* mdct_line,
                         Ipp16s* mdct_line_pred,
                         Ipp32s mdct_scaleFactor,
                         Ipp32s *p_smr)
{
  Ipp32s mdct_line_int[N_LONG/2];
  Ipp32s mdct_line_pred_int[N_LONG/2];
  Ipp32s ltp_long_used[MAX_SFB];
  Ipp16s mdct_line_abs[N_LONG/2];
  Ipp16s mdct_scaled[N_LONG/2];
  Ipp16s mdct_scaled_pred[N_LONG/2];
  Ipp16s x_quant[N_LONG/2];
  Ipp16s scale_factors[MAX_SECTION_NUMBER];
  Ipp16s sfb_cb[MAX_SECTION_NUMBER];
  sDistortionBlock Dist;
  Ipp32s *smr, *px_min, *p_mdct_line_pred_int;
  Ipp32s *sfb_offset;
  Ipp32s sum_dist = 0;         /* stupid MS warning */
  Ipp32s above_dist = 0;       /* stupid MS warning */
  Ipp32s above = 0;            /* stupid MS warning */
  Ipp32s iter = 0;             /* stupid MS warning */
  Ipp32s ltp_data_present = 0; /* stupid MS warning */
  Ipp32s pow34_scaleFact;
  Ipp32s win, sfb, i;
  Ipp32s first, itest;
  Ipp16s max_mdct_line, max_mdct_line_pred;

  ippsAbs_16s(mdct_line, mdct_line_abs, pStream->max_line);
  ippsMax_16s(mdct_line_abs, pStream->max_line, &max_mdct_line);

  if (mdct_line_pred) {
    ippsAbs_16s(mdct_line_pred, mdct_line_abs, pStream->max_line);
    ippsMax_16s(mdct_line_abs, pStream->max_line, &max_mdct_line_pred);

    if (max_mdct_line_pred > max_mdct_line) {
      max_mdct_line = max_mdct_line_pred;
    }
  }

  pow34_scaleFact = 3 * (mdct_scaleFactor >> 2) - 1;

  if (max_mdct_line != 0) {
    Ipp16s tmp_mdct_scaled;
    Ipp32s i_tmp_mdct_scaled;

    ippsPow34_16s_Sfs(&max_mdct_line, mdct_scaleFactor,
                      &tmp_mdct_scaled, pow34_scaleFact, 1);

    i_tmp_mdct_scaled = tmp_mdct_scaled;
    while (i_tmp_mdct_scaled < 16384) {
      i_tmp_mdct_scaled = i_tmp_mdct_scaled << 1;
      pow34_scaleFact--;
    }
  }

  ippsPow34_16s_Sfs(mdct_line, mdct_scaleFactor,
                    mdct_scaled, pow34_scaleFact, pStream->max_line);

  if (pBlock->ns_mode) {
    if (mdct_scaleFactor > 0) {
      for (i = 0; i < pStream->max_line; i++) {
        mdct_line_int[i] = ((Ipp32s)mdct_line[i] << mdct_scaleFactor);
      }
    } else {
      for (i = 0; i < pStream->max_line; i++) {
        mdct_line_int[i] = ((Ipp32s)mdct_line[i] >> -mdct_scaleFactor);
      }
    }

    smr = p_smr;
    sfb_offset = pStream->sfb_offset;
    px_min = Dist.x_min;
    for (win = 0; win < pStream->num_window_groups; win++) {
      for (sfb = 0; sfb < pStream->max_sfb; sfb++) {
        ippsDotProd_16s32s_Sfs(mdct_line + sfb_offset[sfb],
                               mdct_line + sfb_offset[sfb],
                               sfb_offset[sfb+1] - sfb_offset[sfb],
                               px_min + sfb, pBlock->sfb_width_scale - 1);
      }

      ippsMul_32s_ISfs(smr, px_min, pStream->max_sfb, 21);

      sfb_offset += pStream->max_sfb;
      smr += pStream->max_sfb;
      px_min += pStream->max_sfb;
    }
  }

  p_mdct_line_pred_int = 0;

  if (mdct_line_pred) {
    ippsPow34_16s_Sfs(mdct_line_pred, mdct_scaleFactor,
                      mdct_scaled_pred, pow34_scaleFact, pStream->max_line);

    if (pBlock->ns_mode) {
      if (mdct_scaleFactor > 0) {
        for (i = 0; i < pStream->max_line; i++) {
          mdct_line_pred_int[i] = ((Ipp32s)mdct_line_pred[i] << mdct_scaleFactor);
        }
      } else {
        for (i = 0; i < pStream->max_line; i++) {
          mdct_line_pred_int[i] = ((Ipp32s)mdct_line_pred[i] >> mdct_scaleFactor);
        }
      }
      p_mdct_line_pred_int = mdct_line_pred_int;
      smr = p_smr;
      sfb_offset = pStream->sfb_offset;
      px_min = Dist.x_min_pred;
      for (win = 0; win < pStream->num_window_groups; win++) {
        for (sfb = 0; sfb < pStream->max_sfb; sfb++) {
          ippsDotProd_16s32s_Sfs(mdct_line_pred + sfb_offset[sfb],
                                 mdct_line_pred + sfb_offset[sfb],
                                 sfb_offset[sfb+1] - sfb_offset[sfb],
                                 px_min + sfb, pBlock->sfb_width_scale - 1);
        }

        ippsMul_32s_ISfs(smr, px_min, pStream->max_sfb, 21);

        sfb_offset += pStream->max_sfb;
        smr += pStream->max_sfb;
        px_min += pStream->max_sfb;
      }
    }
  }

  if (pBlock->ns_mode) {
    ippsSet_32s(0, Dist.distortion,
      pStream->num_window_groups * pStream->max_sfb);
    ippsSet_16s(0, pBlock->ns_scale_factors,
      pStream->num_window_groups * pStream->max_sfb);
    ippsSet_32s(1, Dist.amplified,
      pStream->num_window_groups * pStream->max_sfb);
  }

  first = 1;
  for (;;) {
    pBlock->start_common_scalefac = -SF_OFFSET;
    pBlock->finish_common_scalefac = -SF_OFFSET;
    max_mdct_line = 0;

    ippsMax_16s(mdct_scaled, pStream->max_line, &max_mdct_line);

    if (mdct_line_pred) {
      ippsMax_16s(mdct_scaled_pred, pStream->max_line, &max_mdct_line_pred);

      if (max_mdct_line_pred > max_mdct_line) {
        max_mdct_line = max_mdct_line_pred;
      }
    }

    if (max_mdct_line > 0) {
      Ipp32s quot = pow34_scaleFact / 3;
      Ipp32s rem = pow34_scaleFact - quot * 3;
      Ipp32s shift, max_quant, j, tmp_quot, tmp;
      Ipp32s delta, add_shift;
      Ipp16s tmp_buf[16];

      if (rem > 0) {
        rem -= 3;
        quot += 1;
      }

      tmp_quot = quot;

      ippsMulC_16s_Sfs(scalefac_pow, max_mdct_line, tmp_buf, 16, 15);

      shift = 0;
      max_quant = (MAX_QUANT << (-rem));

      if (max_quant < (Ipp32s)tmp_buf[0]) {
        max_quant <<= 3;
        tmp_quot++;
      }

      for (j = 15; j >= 0; j--) {
        tmp = (Ipp32s)tmp_buf[j];
        if ((tmp << scalefac_pow_shift[j]) <= max_quant) {
          shift = j;
          break;
        }
      }

      pBlock->start_common_scalefac = tmp_quot * 16 - shift;

      shift = 16;
      delta = (65536 - MAGIC_NUMBER_I) << (2-rem);

      tmp = ((Ipp32s)tmp_buf[15] << scalefac_pow_shift[15]);
      add_shift = 0;

      while (delta > tmp) {
        tmp <<= 3;
        add_shift+=3;
        quot--;
      }

      for (j = 15; j >= 0; j--) {
        tmp = (Ipp32s)tmp_buf[j];
        if ((tmp << (scalefac_pow_shift[j] + add_shift)) < delta) {
          shift = j;
          break;
        }
      }

      if (shift == 16) {
        shift = 15;
        quot++;
      }

      pBlock->finish_common_scalefac = (quot + 6) * 16 - shift + 1;

      if (pBlock->start_common_scalefac > 255 - SF_OFFSET) {
        pBlock->start_common_scalefac = 255 - SF_OFFSET;
      } else if (pBlock->start_common_scalefac < -SF_OFFSET) {
        pBlock->start_common_scalefac = -SF_OFFSET;
      }

      if (pBlock->finish_common_scalefac > 255 - SF_OFFSET) {
        pBlock->finish_common_scalefac = 255 - SF_OFFSET;
      } else if (pBlock->finish_common_scalefac < -SF_OFFSET) {
        pBlock->finish_common_scalefac = -SF_OFFSET;
      }
    }

    aaciencMain_loop(pBlock, pStream, mdct_line, mdct_scaled,
                     mdct_line_pred, mdct_scaled_pred, pow34_scaleFact);

    if (pBlock->ns_mode == 0)
      break;

    aaciencCalcDist(pBlock, pStream, &Dist, mdct_line_int,
                    p_mdct_line_pred_int, mdct_scaleFactor);

    if (first) {
      itest = 1;
      first = 0;
    } else {
      itest = 0;
      if (above > 0) {
        if (Dist.above == 0) itest = 1;
        else if (Dist.above_dist < above_dist) itest = 1;
      } else if ((Dist.sum_dist < sum_dist) && (Dist.above == 0)) itest = 1;
    }

    if (itest) {
      iter = 0;
      sum_dist = Dist.sum_dist;
      above_dist = Dist.above_dist;
      above = Dist.above;
      ippsCopy_16s(pStream->scale_factors, scale_factors,
        pStream->num_window_groups * pStream->max_sfb);
      ippsCopy_16s(pStream->x_quant, x_quant, pStream->max_line);
      ippsCopy_16s(pStream->sfb_cb, sfb_cb, MAX_SECTION_NUMBER);

      if (mdct_line_pred) {
        if (pStream->audioObjectType == AOT_AAC_LTP) {
          ltp_data_present = pStream->ltp_data_present;
          if (pStream->ltp_data_present) {
            ippsCopy_32s(pStream->ltp_long_used, ltp_long_used, MAX_SFB);
          }
        }
      }
    } else iter++;

    if (Dist.max_dist <= 0) break;
    if (Dist.above == 0) break;

    if ((iter > 5) && (above == 0)) {
      break;
    }

    itest = aaciencAdjustSF(pBlock, pStream, &Dist, mdct_scaled,
                            p_mdct_line_pred_int, mdct_scaled_pred,
                            &pow34_scaleFact);

    if (!itest) {
      break;
    }
  }

  if (pBlock->ns_mode) {
    ippsCopy_16s(scale_factors, pStream->scale_factors,
      pStream->num_window_groups * pStream->max_sfb);
    ippsCopy_16s(x_quant, pStream->x_quant, pStream->max_line);
    ippsCopy_16s(sfb_cb, pStream->sfb_cb, MAX_SECTION_NUMBER);

    if (mdct_line_pred) {
      if (pStream->audioObjectType == AOT_AAC_LTP) {
        pStream->ltp_data_present = ltp_data_present;
        if (pStream->ltp_data_present) {
          ippsCopy_32s(ltp_long_used, pStream->ltp_long_used, MAX_SFB);
        }
      }
    }
  }
}

/****************************************************************************/

static void aaciencMain_loop(sQuantizationBlock* pBlock,
                             sEnc_individual_channel_stream* pStream,
                             Ipp16s* mdct_line,
                             Ipp16s* mdct_scaled,
                             Ipp16s* mdct_line_pred,
                             Ipp16s* mdct_scaled_pred,
                             Ipp32s pow34_scaleFact)
{
  Ipp16s tmp_x_quant[N_LONG/2];
  Ipp16s mdct_sign[N_LONG/2];
  Ipp16s mdct_sign_pred[N_LONG/2];
  Ipp16s x_quant_unsigned[N_LONG/2];
  Ipp16s quant_unsigned_pred[N_LONG/2];
  Ipp16s x_quant_signed_pred[N_LONG/2];
  Ipp16s *x_quant_unsigned_pred;
  Ipp32s start_common_scalefac = pBlock->start_common_scalefac;
  Ipp32s finish_common_scalefac = pBlock->finish_common_scalefac;
  Ipp32s common_scalefactor;
  Ipp32s common_scalefactor_update;
  Ipp32s needed_bits, bits_for_scale_factor_data;
  Ipp32s num_scale_factor;
  Ipp32s i, pred;
  Ipp32s index, scale, shift;
  Ipp32s magic_number;

  num_scale_factor = pStream->num_window_groups * pStream->max_sfb;

  pred = 0;
  if (mdct_line_pred) {
    pred = 1;
  }

  for (i = 0; i < pStream->max_line; i++) {
    mdct_sign[i] = SIGN(mdct_line[i]);
  }

  x_quant_unsigned_pred = 0;

  if (pred) {
    for (i = 0; i < pStream->max_line; i++) {
      mdct_sign_pred[i] = SIGN(mdct_line_pred[i]);
    }
    x_quant_unsigned_pred = quant_unsigned_pred;
  }

  common_scalefactor = pBlock->last_frame_common_scalefactor[0];
  common_scalefactor_update = pBlock->common_scalefactor_update[0];

  if (common_scalefactor < start_common_scalefac)
    common_scalefactor = start_common_scalefac;

  if (common_scalefactor > finish_common_scalefac)
    common_scalefactor = finish_common_scalefac;

  for (;;) {
    index = (-common_scalefactor) & 0xF;
    scale = pow34_scaleFact +
      (3 * ((-common_scalefactor) >> 4) + scalefac_pow_shift[index]);

    magic_number = (Ipp32s)MAGIC_NUMBER_I - 32768;

    shift = scale + 16;
    if (scale >= 0) magic_number = 0;
    else if (scale > -16) {
      magic_number = ((magic_number + (1 << (shift - 1))) >> shift);
      shift = 0;
    } else if (scale < -16) {
      shift = -shift;
      scale = -16;
    }

    ippsMulC_16s_Sfs(mdct_scaled, scalefac_pow[index], tmp_x_quant,
                     pStream->max_line, 15 + shift);

    ippsAddC_16s_Sfs(tmp_x_quant, (Ipp16s)magic_number, x_quant_unsigned,
                     pStream->max_line, -scale);

    ippsMul_16s(mdct_sign, x_quant_unsigned, pStream->x_quant, pStream->max_line);

    if (pred) {
      ippsMulC_16s_Sfs(mdct_scaled_pred, scalefac_pow[index], tmp_x_quant,
                       pStream->max_line, 15 + shift);

      ippsAddC_16s_Sfs(tmp_x_quant, (Ipp16s)magic_number,
                       x_quant_unsigned_pred, pStream->max_line, -scale);

      ippsMul_16s(mdct_sign_pred, x_quant_unsigned_pred,
                  x_quant_signed_pred, pStream->max_line);
    }

    needed_bits = best_codebooks_search(pStream, x_quant_unsigned,
                                        pStream->x_quant, x_quant_unsigned_pred,
                                        x_quant_signed_pred);

    if (pBlock->ns_mode) {
      common_scalefactor += SF_OFFSET;
      for (i = 0; i < num_scale_factor; i++) {
        pStream->scale_factors[i] = (Ipp16s)(common_scalefactor -
          pBlock->ns_scale_factors[i]);
      }
      common_scalefactor -= SF_OFFSET;
      bits_for_scale_factor_data = enc_scale_factor_data(pStream, NULL, 0);
    } else {
      bits_for_scale_factor_data = num_scale_factor;

      for (i = 0; i < num_scale_factor; i++) {
        if (pStream->sfb_cb[i] == 0)
          bits_for_scale_factor_data--;
      }
    }

    needed_bits += bits_for_scale_factor_data;

    if (needed_bits == pBlock->available_bits)
      break;

    if (needed_bits > pBlock->available_bits) {
      if (common_scalefactor == finish_common_scalefac)
        break;

      if (common_scalefactor_update < 0) {
        common_scalefactor_update = -common_scalefactor_update;
      }
      common_scalefactor_update = (common_scalefactor_update + 1) >> 1;
    } else {
      if (common_scalefactor == start_common_scalefac)
        break;

      if (common_scalefactor_update == 1)
        break;

      if (common_scalefactor_update > 0) {
        common_scalefactor_update = -common_scalefactor_update;
      }
      common_scalefactor_update >>= 1;
    }

    common_scalefactor += common_scalefactor_update;

    if (common_scalefactor < start_common_scalefac)
      common_scalefactor = start_common_scalefac;

    if (common_scalefactor > finish_common_scalefac)
      common_scalefactor = finish_common_scalefac;

  }

  pBlock->common_scalefactor_update[0] =
    common_scalefactor - pBlock->last_frame_common_scalefactor[0];
  pBlock->last_frame_common_scalefactor[0] = common_scalefactor;

  if (pBlock->common_scalefactor_update[0] >= 0) {
    if (pBlock->common_scalefactor_update[0] <= 2)
      pBlock->common_scalefactor_update[0] = 2;
  } else {
    if (pBlock->common_scalefactor_update[0] >= -2)
      pBlock->common_scalefactor_update[0] = -2;
  }

  pBlock->used_bits = needed_bits;
  common_scalefactor += SF_OFFSET;

  if (!pBlock->ns_mode) {
    for (i = 0; i < num_scale_factor; i++) {
      pStream->scale_factors[i] = (Ipp16s)common_scalefactor;
    }
  }

  if (pred) {
    Ipp32s *pred_used, pred_present;
    Ipp32s max_sfb_pred = pStream->max_sfb;

    pred_present = 0;
    pred_used = 0;

    if (pStream->audioObjectType == AOT_AAC_LTP) {
      pred_present = pStream->ltp_data_present;
      pred_used = pStream->ltp_long_used;
      if (max_sfb_pred > MAX_LTP_SFB_LONG) max_sfb_pred = MAX_LTP_SFB_LONG;
    }

    if (pred_present) {
      Ipp32s sfb;
      for (sfb = 0; sfb < max_sfb_pred; sfb++) {
        if (pred_used[sfb]) {
          Ipp32s begin = pStream->sfb_offset[sfb];
          Ipp32s end = pStream->sfb_offset[sfb+1];

          for (i = begin; i < end; i++) {
            pStream->x_quant[i] = x_quant_signed_pred[i];
          }
        }
      }
    }
  }
}

/****************************************************************************/

#define IFQSTEP 18658
#define SCALED_MAX 28773

/****************************************************************************/

static void aaciencCalcDist(sQuantizationBlock* pBlock,
                            sEnc_individual_channel_stream* pStream,
                            sDistortionBlock* pDist,
                            Ipp32s* mdct_line_int,
                            Ipp32s* mdct_line_pred_int,
                            Ipp32s  mdct_scaleFactor)
{
  Ipp32s mdct_rqnt[1024];
  Ipp32s *px_min = pDist->x_min;
  Ipp32s *px_min_pred = pDist->x_min_pred;
  Ipp32s *dist = pDist->distortion;
  Ipp32s *pred_used, pred_used_buf[MAX_SECTION_NUMBER];
  Ipp32s *saved_pred_used = pDist->saved_pred_used;
  Ipp32s *amplified = pDist->amplified;
  Ipp32s *sfb_offset = pStream->sfb_offset;
  Ipp16s *scale_factors = (Ipp16s*)pStream->scale_factors;
  Ipp16s *saved_scale_factors = (Ipp16s*)pDist->saved_scale_factors;
  Ipp16s *ns_scale_factors = pBlock->ns_scale_factors;
  Ipp32s shift = 2 * mdct_scaleFactor + pBlock->sfb_width_scale - 1;
  Ipp64s noise64;
  Ipp32s noise;
  Ipp32s i, sfb, win;
  Ipp16s dist_max16;

  if (mdct_line_pred_int) {
    Ipp32s pred_present;

    pred_present = 0;
    pred_used = 0;

    if (pStream->audioObjectType == AOT_AAC_LTP) {
      if (pStream->ltp_data_present) {
        pred_used = pStream->ltp_long_used;
      } else {
        pred_used = pred_used_buf;
        for (sfb = 0; sfb < pStream->max_sfb; sfb++) {
          pred_used[sfb] = 0;
        }
      }
    } else {
      pred_used = pred_used_buf;
      for (sfb = 0; sfb < pStream->max_sfb; sfb++) {
        pred_used[sfb] = 0;
      }
    }
  } else {
    pred_used = pred_used_buf;
    for (sfb = 0; sfb < MAX_SECTION_NUMBER; sfb++) {
      pred_used[sfb] = 0;
    }
  }

  pDist->above = 0;
  pDist->max_dist = 0;
  pDist->sum_dist = 0;
  pDist->above_dist = 0;

  for (win = 0; win < pStream->num_window_groups; win++) {
    for (sfb = 1; sfb < pStream->ath_max_sfb; sfb++) {
      Ipp32s sfb_start = sfb_offset[sfb];
      Ipp32s width = sfb_offset[sfb+1] - sfb_offset[sfb];

      if ((scale_factors[sfb] != saved_scale_factors[sfb]) ||
          (pred_used[sfb] != saved_pred_used[sfb]) ||
          (amplified[sfb])) {

        ippsPow43Scale_16s32s_Sf(pStream->x_quant, mdct_rqnt,
                                  scale_factors + sfb, sfb_offset + sfb,
                                  SF_OFFSET, 1, 1, 0);
        if (pred_used[sfb]) {
          noise64 = 0;
          for (i = 0; i < width; i++) {
            Ipp32s temp;
            temp = mdct_line_pred_int[sfb_start+i] - mdct_rqnt[i];
            noise64 += temp * (Ipp64s)temp;
          }
        } else {
          noise64 = 0;
          for (i = 0; i < width; i++) {
            Ipp32s temp;
            temp = mdct_line_int[sfb_start+i] - mdct_rqnt[i];
            noise64 += temp * (Ipp64s)temp;
          }
        }

        if (shift > 0) {
          noise = (Ipp32s)(noise64 >> shift);
        } else {
          noise = (Ipp32s)(noise64 << -shift);
        }

        dist[sfb] = 0;
        if (px_min_pred[sfb] > 0)
          ippsDiv_32s_Sfs(px_min_pred + sfb, &noise, dist + sfb, 1, -20);

        amplified[sfb] = 0;
        saved_scale_factors[sfb] = scale_factors[sfb];
        saved_pred_used[sfb] = pred_used[sfb];
      }

      pDist->sum_dist += dist[sfb];


      if (dist[sfb] >= 1) {
        pDist->above++;
        pDist->above_dist += dist[sfb];
      }

      if (dist[sfb] > pDist->max_dist) {
        pDist->max_dist = dist[sfb];
      }
    }

    scale_factors += pStream->max_sfb;
    saved_scale_factors += pStream->max_sfb;
    ns_scale_factors += pStream->max_sfb;
    sfb_offset += pStream->max_sfb;
    px_min += pStream->max_sfb;
    px_min_pred += pStream->max_sfb;
    dist += pStream->max_sfb;
    pred_used += pStream->max_sfb;
    saved_pred_used += pStream->max_sfb;
    amplified += pStream->max_sfb;
  }

  if (pDist->max_dist > (1 << 20))  {
    ippsSqrt_32s16s_Sfs(&pDist->max_dist, &dist_max16, 1, 0);
    pDist->max_dist = ((Ipp32s)dist_max16 << 10);
  }
}

/****************************************************************************/

static Ipp32s aaciencAdjustSF(sQuantizationBlock* pBlock,
                              sEnc_individual_channel_stream* pStream,
                              sDistortionBlock* pDist,
                              Ipp16s* mdct_scaled,
                              Ipp32s* mdct_line_pred_int,
                              Ipp16s* mdct_scaled_pred,
                              Ipp32s* pow34_scaleFact)
{
  Ipp32s *dist = pDist->distortion;
  Ipp32s *sfb_offset = pStream->sfb_offset;
  Ipp32s *amplified = pDist->amplified;
  Ipp16s *ns_scale_factors = pBlock->ns_scale_factors;
  Ipp32s sca_min, sca_max, sca_min_pred, sca_max_pred;
  Ipp32s sfb, win;
  Ipp32s ret_flag = 0;
  Ipp16s sca_min16, sca_max16;

  for (win = 0; win < pStream->num_window_groups; win++) {
    for (sfb = 1; sfb < pStream->ath_max_sfb; sfb++) {
      if (dist[sfb] >= pDist->max_dist) {
        if (ns_scale_factors[sfb] >= 5) {
          return 0;
        } else {
          Ipp32s sfb_start = sfb_offset[sfb];
          Ipp32s width = sfb_offset[sfb+1] - sfb_offset[sfb];

          ns_scale_factors[sfb] += 1;
          amplified[sfb] = 1;

          ippsMinMax_16s(mdct_scaled + sfb_start, width, &sca_min16, &sca_max16);
          sca_min = -((Ipp32s)sca_min16);
          sca_max = sca_max16;

          if (sca_min > sca_max) {
            sca_max = sca_min - 1;
          }

          if (mdct_line_pred_int) {
            ippsMinMax_16s(mdct_scaled_pred + sfb_start, width, &sca_min16, &sca_max16);
            sca_min_pred = -((Ipp32s)sca_min16);
            sca_max_pred = sca_max16;

            if (sca_min_pred > sca_max_pred) {
              sca_max_pred = sca_min_pred - 1;
            }

            if (sca_max_pred > sca_max) {
              sca_max = sca_max_pred;
            }
          }

          if (sca_max >= SCALED_MAX) {
            *pow34_scaleFact = *pow34_scaleFact + 1;
            ippsRShiftC_16s_I(1, mdct_scaled, pStream->max_line);
            if (mdct_line_pred_int) {
              ippsRShiftC_16s_I(1, mdct_scaled_pred,pStream->max_line);
            }
          }

          ippsMulC_16s_ISfs(IFQSTEP, mdct_scaled + sfb_start, width, 14);

          if (mdct_line_pred_int) {
            ippsMulC_16s_ISfs(IFQSTEP, mdct_scaled_pred + sfb_start, width, 14);
          }
        }
      }
      if (ns_scale_factors[sfb] == 0) {
        ret_flag = 1;
      }
    }
    ns_scale_factors += pStream->max_sfb;
    sfb_offset += pStream->max_sfb;
    dist += pStream->max_sfb;
    amplified += pStream->max_sfb;
  }

  return ret_flag;
}

#endif //UMC_ENABLE_AAC_INT_AUDIO_ENCODER

